package demo.SOAPClient;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.MalformedURLException;
import java.rmi.RemoteException;
import java.util.StringTokenizer;
import java.util.HashMap;

import javax.xml.rpc.ServiceException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;

import fedora.client.FedoraClient;
import fedora.server.management.FedoraAPIM;
import fedora.server.access.FedoraAPIA;
import fedora.server.types.gen.DatastreamDef;
import fedora.server.types.gen.MethodParmDef;
import fedora.server.types.gen.MIMETypedStream;
import fedora.server.types.gen.ObjectMethodsDef;
import fedora.server.types.gen.ObjectProfile;
import fedora.server.types.gen.RepositoryInfo;
import fedora.server.types.gen.Property;

/**
 *
 * <p><b>Title:</b> DemoSOAPClient</p>
 * <p><b>Description: A simple example on how to write a SOAP client that
 * makes calls to the Fedora SOAP interfaces (API-A and API-M).</b> </p>
 *
 * -----------------------------------------------------------------------------
 *
 * <p><b>License and Copyright: </b>The contents of this file are subject to the
 * Mozilla Public License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of the License
 * at <a href="http://www.mozilla.org/MPL">http://www.mozilla.org/MPL/.</a></p>
 *
 * <p>Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.</p>
 *
 * <p>The entire file consists of original code.  Copyright &copy; 2002-2004 by The
 * Rector and Visitors of the University of Virginia and Cornell University.
 * All rights reserved.</p>
 *
 * -----------------------------------------------------------------------------
 *
 * @author payette@cs.cornell.edu
 */
public class DemoSOAPClient {

    private static FedoraAPIM APIM;
	private static FedoraAPIA APIA;
    private static HashMap s_repoInfo=new HashMap();

    public DemoSOAPClient(String protocol, String host, int port, String user, String pass)
            throws Exception {
            	
        // Use the FedoraClient utility to get SOAP stubs.
        // These SOAP stubs enable the client to connect to a Fedora repository
        // via the API-A and API-M web service interfaces.

		String baseURL = protocol + "://" + host + ":" + port + "/fedora";
		FedoraClient fc = new FedoraClient(baseURL, user, pass);
		APIA=fc.getAPIA();
		APIM=fc.getAPIM();
    }
    

    
	public RepositoryInfo describeRepository() 
		throws RemoteException {
			
		// make the SOAP call on API-A using the connection stub
		RepositoryInfo repoinfo = APIA.describeRepository();
		
		// print results
		System.out.println("SOAP Request: describeRepository...");
		System.out.println("SOAP Response: repository version = " + repoinfo.getRepositoryVersion());
		System.out.println("SOAP Response: repository name = " + repoinfo.getRepositoryName());
		System.out.println("SOAP Response: repository pid namespace = " + repoinfo.getRepositoryPIDNamespace());	
		System.out.println("SOAP Response: repository default export = " + repoinfo.getDefaultExportFormat());
		System.out.println("SOAP Response: repository base URL = " + repoinfo.getRepositoryBaseURL());	
		System.out.println("SOAP Response: repository OAI namespace = " + repoinfo.getOAINamespace());
		System.out.println("SOAP Response: repository sample OAI identifier = " + repoinfo.getSampleOAIIdentifier());
		System.out.println("SOAP Response: repository sample OAI URL = " + repoinfo.getSampleOAIURL());
		System.out.println("SOAP Response: repository sample access URL = " + repoinfo.getSampleAccessURL());
		System.out.println("SOAP Response: repository sample search URL = " + repoinfo.getSampleSearchURL());		
		System.out.println("SOAP Response: repository sample PID = " + repoinfo.getSamplePID());
		return repoinfo;						
	}
	
	public String ingest(InputStream ingestStream, String ingestFormat, String logMessage)
		throws RemoteException, IOException {
		
		// prep 		
		ByteArrayOutputStream out=new ByteArrayOutputStream();
		pipeStream(ingestStream, out, 4096);
		
		// make the SOAP call on API-M using the connection stub
		String pid = APIM.ingest(out.toByteArray(), ingestFormat, logMessage);
		
		System.out.println("SOAP Request: ingest...");
		System.out.println("SOAP Response: pid = " + pid);
		return pid;
	}
	public String addDatastream(String pid, String dsID, String[] altIDs, String dsLabel, 
		boolean versionable, String dsMIME, String formatURI, 
		String dsLocation, String dsControlGroup, String dsState, 
		String checksumType, String checksum, String logMessage)
		throws RemoteException {

			// make the SOAP call on API-M using the connection stub			
			String datastreamID = APIM.addDatastream(
				pid, dsID, altIDs, dsLabel, versionable, dsMIME, formatURI,
				dsLocation, dsControlGroup, dsState, checksumType, checksum, logMessage);
				
			System.out.println("SOAP Request: addDatastream...");
			System.out.println("SOAP Response: datastreamID = " + datastreamID);				
			return datastreamID;
		}
		
	public String modifyDatastreamByReference(String pid, String dsID, String[] altIDs, String dsLabel, 
		String dsMIME, String formatURI, String dsLocation,
		String checksumType, String checksum,
		String logMessage, boolean force)
		throws RemoteException {

			// make the SOAP call on API-M using the connection stub
			String datastreamID = APIM.modifyDatastreamByReference(
				pid, dsID, altIDs, dsLabel, dsMIME,
				formatURI, dsLocation, checksumType, checksum, logMessage, force);			
				
			System.out.println("SOAP Request: modifyDatastreamByReference...");
			System.out.println("SOAP Response: datastreamID = " + datastreamID);				
			return datastreamID;
		}
		
	public String[] purgeDatastream(String pid, String dsID, String startDate, String endDate, 
		String logMessage, boolean force)
		throws RemoteException {

			// make the SOAP call on API-M using the connection stub
			String[] dateTimeStamps = APIM.purgeDatastream(
				pid, dsID, startDate, endDate, logMessage, force);			
				
			System.out.println("SOAP Request: purgeDatastream...");				
			return dateTimeStamps;
		}
		
	public String purgeObject(String pid, String logMessage, boolean force)
		throws RemoteException {

			// make the SOAP call on API-M using the connection stub
			String purgeDateTime = APIM.purgeObject(pid, logMessage, force);			
				
			System.out.println("SOAP Request: purgeObject...");
			System.out.println("SOAP Response: purge dateTime = " + purgeDateTime);				
			return purgeDateTime;
		}
		
	public byte[] export(String pid, String format, String exportContext, OutputStream outStream) 
		throws RemoteException, IOException {
					
		// make the SOAP call on API-M
		byte[] objectXML = APIM.export(pid, format, exportContext);
		
		// serialize the object XML to the specified output stream
		try {						
			// use Xerces to pretty print the xml, assuming it's well formed
			DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
			factory.setNamespaceAware(true);
			DocumentBuilder builder=factory.newDocumentBuilder();
			Document doc=builder.parse(new ByteArrayInputStream(objectXML));
			OutputFormat fmt=new OutputFormat("XML", "UTF-8", true);
			fmt.setIndent(2);
			fmt.setLineWidth(120);
			fmt.setPreserveSpace(false);
			XMLSerializer ser=new XMLSerializer(outStream, fmt);
			ser.serialize(doc);
		} catch (Exception e) {
			System.out.println("Error on export while serializing object XML." + 
				e.getClass().getName() + " : " + e.getMessage());
		} finally {
		  outStream.close();
		}
		
		// print results
		System.out.println("SOAP Request: export...");
		System.out.println("SOAP Response: see result serialized in XML export file.");	
		return objectXML;						
	}

	public byte[] getObjectXML(String pid) 
		throws RemoteException {
			
		// make the SOAP call on API-M	
		byte[] objectXML = APIM.getObjectXML(pid);
		
		// print results

		return objectXML;						
	}
	
	/**
	 * Copies the contents of an InputStream to an OutputStream, then closes
	 * both.  
	 *
	 * @param in The source stream.
	 * @param out The target stram.
	 * @param bufSize Number of bytes to attempt to copy at a time.
	 * @throws IOException If any sort of read/write error occurs on either
	 *         stream.
	 */
	public static void pipeStream(InputStream in, OutputStream out, int bufSize)
			throws IOException {
		try {
			byte[] buf = new byte[bufSize];
			int len;
			while ( ( len = in.read( buf ) ) > 0 ) {
				out.write( buf, 0, len );
			}
		} finally {
			try {
				in.close();
				out.close();
			} catch (IOException e) {
				System.err.println("WARNING: Could not close stream.");
			}
		}
	}
		
	public static void listDatastreams() throws Exception {
		
		DatastreamDef[] dsDefs = APIA.listDatastreams("demo:11", null);
		System.out.println("SOAP Request: listDatastreams...");
		System.out.println("SOAP Response: see results below.");			
		verifyDatastreamDefs(dsDefs, "SOAP Response: listDatastream: ");
	}	
	
	public static void listMethods() throws Exception {
		
		ObjectMethodsDef[] methodDefs = APIA.listMethods("demo:11", null);
		System.out.println("SOAP Request: listMethods...");
		System.out.println("SOAP Response: see results below.");
		verifyObjectMethods(methodDefs, "SOAP Response: listMethods: ");
	}	
	
	public static void getDatastreamDissemination() throws Exception {
		
		// test for DC datastream
		MIMETypedStream ds = null;
		ds = APIA.getDatastreamDissemination("demo:11", "DC", null);
		System.out.println("SOAP Request: getDatastreamDissemination for DC datastream of demo object demo:11...");		
		String dsXML = new String(ds.getStream(), "UTF-8");
		System.out.println("SOAP Response: GetDatastreamDissemination Object:demo:11 Datastream:DC succeeded.");
		System.out.println("SOAP Response: DC datastream contents: \n"+dsXML);
		
		// test for type X datastream 		
		ds = APIA.getDatastreamDissemination("demo:11", "TECH1", null);
		System.out.println("\nSOAP Request: getDatastreamDissemination for TECH1 datastream of demo object demo:11...");		
		dsXML = new String(ds.getStream(), "UTF-8");
		System.out.println("SOAP Response: GetDatastreamDissemination Object:demo:11 Datastream:TECH1 succeeded.");
		System.out.println("SOAP Response: TECH1 datastream contents: \n"+dsXML);		

		// test for type E datastream 			
		ds = APIA.getDatastreamDissemination("demo:11", "DS1", null);	
		System.out.println("\nSOAP Request: getDatastreamDissemination for DS1 datastream of demo object demo:11...");			
		System.out.println("SOAP Response: GetDatastreamDissemination Object:demo:11 Datastream:DS1 succeeded.");
		System.out.println("SOAP Response: DS1 datastream contents: BINARY DATA "+ds);
		
		// test for type R datastream 			
		ds = APIA.getDatastreamDissemination("demo:30", "DS1", null);
		System.out.println("\nSOAP Request: getDatastreamDissemination for DS1 datastream of demo object demo:30...");			
		System.out.println("SOAP Response: GetDatastreamDissemination Object:demo:30 Datastream:DS1 succeeded.");
		System.out.println("SOAP Response: DS1 datastream contents: BINARY DATA "+ds);
		
		// test for type M datastream 			
		ds = APIA.getDatastreamDissemination("demo:5", "DS1", null);
		System.out.println("\nSOAP Request: getDatastreamDissemination for DS1 datastream of demo object demo:5...");			
		System.out.println("SOAP Response: GetDatastreamDissemination Object:demo:5 Datastream:DS1 succeeded.");
		System.out.println("SOAP Response: DS1 datastream contents: BINARY DATA "+ds);
		
	}	
	

	public static void getObjectProfile() throws Exception {
		ObjectProfile profile = APIA.getObjectProfile("demo:11", null);
		System.out.println("SOAP Request: getObjectProfile for demo object demo:11...");			
		System.out.println("SOAP Response: PID: "+profile.getPid());
		System.out.println("SOAP Response: ObjectType: "+profile.getObjType());
		System.out.println("SOAP Response: ObjectLabel: "+profile.getObjLabel());
		System.out.println("SOAP Response: CreateDate: "+profile.getObjCreateDate());
		System.out.println("SOAP Response: LastModDate: "+profile.getObjLastModDate());
		System.out.println("SOAP Response: ContentModel: "+profile.getObjContentModel());
		System.out.println("SOAP Response: DissIndexViewURL: "+profile.getObjDissIndexViewURL());
		System.out.println("SOAP Response: ItemIndexViewURL: "+profile.getObjItemIndexViewURL());

	}

	public static void getObjectHistory() throws Exception {
		String[] timestamps = APIA.getObjectHistory("demo:11");
		System.out.println("SOAP Request: getObjectHistory for demo object demo:11...");
		for (int i=0; i<timestamps.length; i++) {
			System.out.println("SOAP Response: object:demo:11 changeDate["+i+"]: "+timestamps[i]);
		}
	}

	public static void getDissemination() throws Exception {
	
	// test dissemination of the Default Disseminator
	MIMETypedStream diss = null;
	diss = APIA.getDissemination("demo:5", "fedora-system:3", "viewDublinCore", new Property[0], null);
	System.out.println("SOAP Request: getDissemination for method viewDublinCore of demo object demo:11...");	
	String dsXML = new String(diss.getStream(), "UTF-8");
	System.out.println("SOAP Response: GetDissemination Object:demo:11 Method:viewDublinCore succeeded.");
	System.out.println("SOAP Response: Dissemination results: \n"+dsXML);	
	
	// test dissemination of getThumb method with no parameters
    diss = APIA.getDissemination("demo:5", "demo:1", "getThumbnail", new Property[0], null);
	System.out.println("\nSOAP Request: getDissemination for method getThumbnail of demo object demo:5...");		    
	System.out.println("SOAP Response: GetDissemination Object:demo:5 Method:getThumbnail succeeded.");
	System.out.println("SOAP Response: Dissemination results: BINARY DATA "+diss);    
	
	// test dissemination using resizeImage method with parameters		
	Property[] parms = new Property[4];
	Property p = new Property();
	p.setName("width");
	p.setValue("100");
	parms[0] = p;
	Property p2 = new Property();
	p2.setName("height");
	p2.setValue("100");
	parms[1] = p2;
	Property p3 = new Property();
	p3.setName("x");
	p3.setValue("100");
	parms[2] = p3;
	Property p4 = new Property();
	p4.setName("y");
	p4.setValue("100");
	parms[3] = p4;	
	diss = APIA.getDissemination("demo:29", "demo:27", "cropImage", parms, null);
	System.out.println("\nSOAP Request: getDissemination for method cropImage of demo object demo:29...");		
	System.out.println("SOAP Response: GetDissemination Object:demo:29 Method:cropImage succeeded.");
	System.out.println("SOAP Response: Dissemination results: BINARY DATA "+diss);

	}	
	
	public static void verifyDatastreamDefs(DatastreamDef[] dsDefArray, String msg) throws Exception {
		
		String dsID = null;
		String label = null;
		String mimeType = null;
		DatastreamDef dsDef = null;
	        
		for (int i=0; i<dsDefArray.length; i++) {
			dsDef = dsDefArray[i];
			dsID = dsDef.getID();
			label = dsDef.getLabel();
			mimeType = dsDef.getMIMEType();
			System.out.println(msg + " datastreamDef["+i+"] "
				 + "dsID: "+dsID);
			System.out.println(msg + " datastreamDef["+i+"] "
				 + "label: '"+label+"'");
			System.out.println(msg + " datastreamDef["+i+"] "
				 + "mimeType: "+mimeType);
		}
	        
	}

	public static void verifyObjectMethods(ObjectMethodsDef[] methodDefsArray, String msg) throws Exception {
	
		String bDefPID = null;
		String methodName = null;
		MethodParmDef[] parms = null;
		ObjectMethodsDef methodDef = null;
	        
		for (int i=0; i<methodDefsArray.length; i++) {
			methodDef = methodDefsArray[i];
			bDefPID = methodDef.getBDefPID();
			methodName = methodDef.getMethodName();
			parms = methodDef.getMethodParmDefs();
			System.out.println(msg + " methodDef["+i+"] "
				 + "bDefPID: "+bDefPID);
			System.out.println(msg + " methodDef["+i+"] "
				 + "methodName: '"+methodName+"'");
			for (int j=0; j<parms.length; j++) {
				MethodParmDef p = parms[j];	
				System.out.println(msg + " methodDef["+i+"] "
				 	+ "parmName["+j+"] "+p.getParmName());
			}
		}
	        
	}
	
}
